package com.googlecode.uipipedream.impl;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Event;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Rectangle;
import java.util.Observable;
import java.util.Observer;

import com.googlecode.uipipedream.spec.ColourModel;

/**
 * Implementation of the H, S and B slider bars
 */
public class VerticalSliderBar extends Canvas implements Observer {
	private static final long serialVersionUID = -5668151225831548319L;
	
	protected Rectangle box;
	protected ColourModel model;
	protected boolean draggingBox = false;
	public static final int HUE = 1, SATURATION = 2, BRIGHTNESS = 3;
	protected int type;
	Dimension offDimension;
	Image offImage;
	Graphics offGraphics = null;
	
	@Override public void update(Observable o, Object arg) { repaint(); }
	@Override public synchronized void paint(Graphics g) { update(g); }
	@Override public Dimension preferredSize() { return new Dimension(30, 80); }
	@Override public Dimension minimumSize() { return new Dimension(30, 80); }
	
	public VerticalSliderBar(ColourModel model, int type) {
		this.model = model;
		this.type = type;
		this.model.addObserver(this);
		this.setBackground(Color.black);
	}

	public synchronized void update(Graphics g) {
		// 0. Cache dimensions and setup object.
		Dimension d = getSize();
		if ((offGraphics == null) || (d.width != offDimension.width)
				|| (d.height != offDimension.height)) {
			offDimension = d;
			offImage = createImage(d.width, d.height);
			offGraphics = offImage.getGraphics();
			// border of 4 all round, 8 at left
			box = new Rectangle(8, 4, d.width - 12, d.height - 8);
		}

		// 1. Erase the previous image.
		offGraphics.setColor(getBackground());
		offGraphics.fillRect(0, 0, d.width, d.height);
		offGraphics.setColor(Color.black);

		// 2. Paint the brightness bar
		for (int i = 0; i < box.height; ++i) {
			float offset = 1f - (float) i / box.height;
			switch (type) {
			case HUE:
				offGraphics.setColor(Color.getHSBColor(offset,model.getSaturation(), model.getBrightness()));
				break;
			case SATURATION:
				offGraphics.setColor(Color.getHSBColor(model.getHue(), offset, model.getBrightness()));
				break;
			case BRIGHTNESS:
				offGraphics.setColor(Color.getHSBColor(model.getHue(), model.getSaturation(), offset));
				break;
			}
			offGraphics.drawLine(box.x, box.y + i, box.x + box.width - 1, box.y + i);
		}
		offGraphics.setColor(Color.white);
		offGraphics.drawRect(box.x, box.y, box.width - 1, box.height - 1);

		// 3. Paint brightness cursor
		float f = 0;
		switch (type) {
		case HUE:
			f = model.getHue();
			break;
		case SATURATION:
			f = model.getSaturation();
			break;
		case BRIGHTNESS:
			f = model.getBrightness();
			break;
		}
		int yy = box.y + (int) (box.height * (1.0 - f));
		offGraphics.drawLine(box.x, yy, box.x + box.width - 1, yy);
		offGraphics.drawLine(box.x - 2, yy, box.x - 7, yy + 3);
		offGraphics.drawLine(box.x - 2, yy, box.x - 7, yy - 3);
		offGraphics.drawLine(box.x - 7, yy - 3, box.x - 7, yy + 3);

		g.drawImage(offImage, 0, 0, this);
	}

	public boolean mouseDrag(Event e, int x, int y) {
		if (draggingBox) {
			float f = 1f - (float) (y - box.y) / box.height;
			if (f < 0) f = 0f;
			if (f > 1f) f = 1f;
			switch (type) {
			case HUE:
				model.setHue(f);
				break;
			case SATURATION:
				model.setSaturation(f);
				break;
			case BRIGHTNESS:
				model.setBrightness(f);
				break;
			}
			return true;
		}
		return false;
	}

	public boolean mouseDown(Event e, int x, int y) {
		draggingBox = false;

		if (box.contains(x, y)) {
			draggingBox = true;
			float f = 1f - (float) (y - box.y) / box.height;
			switch (type) {
			case HUE:
				model.setHue(f);
				break;
			case SATURATION:
				model.setSaturation(f);
				break;
			case BRIGHTNESS:
				model.setBrightness(f);
				break;
			}
			return true;
		}
		float f = 0;
		switch (type) {
		case HUE:
			f = model.getHue();
			break;
		case SATURATION:
			f = model.getSaturation();
			break;
		case BRIGHTNESS:
			f = model.getBrightness();
			break;
		}
		int dy = y - box.y - (int) (box.height * (1f - f));
		if (x < box.x && x >= box.x - 8 && dy >= -4 && dy <= 4) {
			draggingBox = true;
			return true;
		}
		return false;
	}

}